

#include "Cache.h"

Cache::Cache(){
	timer = new Timer();
}

Cache::~Cache(){


	//cout << "Memory: Cache: start erasing cache..." << endl;
	//for (boost::unordered_map<key_t, std::pair<PubPkt*, IndexType* > >::iterator it = cacheTable.begin(); it!=cacheTable.end(); it++){
	//	remove(make_pair(it->second.first->getTimeStamp().getTimeVal(), it->first));
	//}
	//cout << "Memory: Cache: cache erased." << endl;

}

bool Cache::insert(PubPkt *pkt, key_t k){

	timer->start();

	IndexType* pointerToIndex;
	IndexType* currentIndexMap;

	boost::unordered_map<int, IndexType*>::iterator it = indexMap.find(pkt->getEventType());
	if (it != indexMap.end()){
	//	cout << "Tipo già presente nell'indice" << endl;
		currentIndexMap = it->second;
	} else {
	//	cout << "Tipo non trovato nell'indice" << endl;
		IndexType* indexType = (IndexType*)malloc(sizeof(IndexType));
		if (pkt->getAttributesNum()>0) {
		//	cout << "Creazione primo INDICE tipo: ";
			indexType->contentType = INDEX;
			indexType->valType = pkt->getAttribute(0).type;
			if (indexType->valType==INT) indexType->intToIndex = new std::map<int, IndexTypeStruct* >();
			if (indexType->valType==FLOAT) indexType->floatToIndex = new std::map<float, IndexTypeStruct* >();
			if (indexType->valType==BOOL) indexType->boolToIndex = new boost::unordered_map<bool, IndexTypeStruct* >();
			if (indexType->valType==STRING) indexType->stringToIndex = new boost::unordered_map<std::string, IndexTypeStruct* >();
		//	cout << indexType->valType << endl;
		}
		else {
		//	cout << "Creazione TERMINALE" << endl;
			indexType->contentType = TERMINAL;
			indexType->keySet = new std::set<std::pair<timestamp_t, key_t> >();
		}
		indexType->prev = NULL;
		std::pair<boost::unordered_map<int, IndexType*>::iterator, bool > ret = indexMap.insert(make_pair(pkt->getEventType(), indexType));
		indexType->itToType = ret.first;
		currentIndexMap = indexType;
	}

	for (int i=0; i<=pkt->getAttributesNum(); i++){
		if (currentIndexMap->contentType == TERMINAL){
		//	cout << "Inserimento della key nel TERMINALE" << endl;
			currentIndexMap->keySet->insert(make_pair(pkt->getTimeStamp().getTimeVal(), k));
			cout << "Memory: Cache: insertion into index completed." << endl;
			pointerToIndex = currentIndexMap;
			break;
		}
		if (currentIndexMap->contentType == INDEX){
			if (currentIndexMap->valType == INT){
				std::map<int, IndexTypeStruct* >::iterator itCurr = currentIndexMap->intToIndex->find(pkt->getIntAttributeVal(i));
				if (itCurr != currentIndexMap->intToIndex->end()){
			//		cout << "Valore di tipo INT già presente nell'indice" << endl;
					currentIndexMap = itCurr->second;
				} else {
			//		cout << "Valore di tipo INT non trovato nell'indice" << endl;
					IndexType* indexType = (IndexType*)malloc(sizeof(IndexType));
					if (i<pkt->getAttributesNum()-1) {
			//			cout << "Creazione nuovo INDICE tipo: ";
						indexType->contentType = INDEX;
						indexType->valType = pkt->getAttribute(i+1).type;
					//	cout << indexType->valType << endl;
						if (indexType->valType==INT) indexType->intToIndex = new std::map<int, IndexTypeStruct* >();
						if (indexType->valType==FLOAT) indexType->floatToIndex = new std::map<float, IndexTypeStruct* >();
						if (indexType->valType==BOOL) indexType->boolToIndex = new boost::unordered_map<bool, IndexTypeStruct* >();
						if (indexType->valType==STRING) indexType->stringToIndex = new boost::unordered_map<std::string, IndexTypeStruct* >();
					}
					else {
						indexType->contentType = TERMINAL;
						indexType->keySet = new std::set<std::pair<timestamp_t, key_t> >();
			//			cout << "Creazione TERMINALE" << endl;
					}
					indexType->prev = currentIndexMap;
					std::pair<std::map<int, IndexTypeStruct* >::iterator, bool> ret = currentIndexMap->intToIndex->insert(make_pair(pkt->getIntAttributeVal(i), indexType));
					indexType->itIntToIndex = ret.first;
					currentIndexMap = indexType;
				}
				continue;
			}
			if (currentIndexMap->valType == FLOAT){
				std::map<float, IndexTypeStruct* >::iterator itCurr = currentIndexMap->floatToIndex->find(pkt->getFloatAttributeVal(i));
				if (itCurr != currentIndexMap->floatToIndex->end()){
			//		cout << "Valore di tipo FLOAT già presente nell'indice" << endl;
					currentIndexMap = itCurr->second;
				} else {
			//		cout << "Valore di tipo FLOAT non trovato nell'indice" << endl;
					IndexType* indexType = (IndexType*)malloc(sizeof(IndexType));
					if (i<pkt->getAttributesNum()-1) {
				//		cout << "Creazione nuovo INDICE tipo: ";
						indexType->contentType = INDEX;
						indexType->valType = pkt->getAttribute(i+1).type;
		//				cout << indexType->valType << endl;
						if (indexType->valType==INT) indexType->intToIndex = new std::map<int, IndexTypeStruct* >();
						if (indexType->valType==FLOAT) indexType->floatToIndex = new std::map<float, IndexTypeStruct* >();
						if (indexType->valType==BOOL) indexType->boolToIndex = new boost::unordered_map<bool, IndexTypeStruct* >();
						if (indexType->valType==STRING) indexType->stringToIndex = new boost::unordered_map<std::string, IndexTypeStruct* >();
					}
					else {
						indexType->contentType = TERMINAL;
						indexType->keySet = new std::set<std::pair<timestamp_t, key_t> >();
			//			cout << "Creazione TERMINALE" << endl;
					}
					indexType->prev = currentIndexMap;
					std::pair<std::map<float, IndexTypeStruct* >::iterator, bool> ret = currentIndexMap->floatToIndex->insert(make_pair(pkt->getFloatAttributeVal(i), indexType));
					indexType->itFloatToIndex = ret.first;
					currentIndexMap = indexType;
				}
				continue;
			}
			if (currentIndexMap->valType == BOOL){
				boost::unordered_map<bool, IndexTypeStruct* >::iterator itCurr = currentIndexMap->boolToIndex->find(pkt->getBoolAttributeVal(i));
				if (itCurr != currentIndexMap->boolToIndex->end()){
		//			cout << "Valore di tipo BOOL già presente nell'indice" << endl;
					currentIndexMap = itCurr->second;
				} else {
		//			cout << "Valore di tipo BOOL non trovato nell'indice" << endl;
					IndexType* indexType = (IndexType*)malloc(sizeof(IndexType));
					if (i<pkt->getAttributesNum()-1) {
	//					cout << "Creazione nuovo INDICE tipo: ";
						indexType->contentType = INDEX;
						indexType->valType = pkt->getAttribute(i+1).type;
	//					cout << indexType->valType << endl;
						if (indexType->valType==INT) indexType->intToIndex = new std::map<int, IndexTypeStruct* >();
						if (indexType->valType==FLOAT) indexType->floatToIndex = new std::map<float, IndexTypeStruct* >();
						if (indexType->valType==BOOL) indexType->boolToIndex = new boost::unordered_map<bool, IndexTypeStruct* >();
						if (indexType->valType==STRING) indexType->stringToIndex = new boost::unordered_map<std::string, IndexTypeStruct* >();
					}
					else {
						indexType->contentType = TERMINAL;
						indexType->keySet = new std::set<std::pair<timestamp_t, key_t> >();
		//				cout << "Creazione TERMINALE" << endl;
					}
					indexType->prev = currentIndexMap;
					std::pair<boost::unordered_map<bool, IndexTypeStruct* >::iterator, bool> ret = currentIndexMap->boolToIndex->insert(make_pair(pkt->getBoolAttributeVal(i), indexType));
					indexType->itBoolToIndex = ret.first;
					currentIndexMap = indexType;
				}
				continue;
			}
			if (currentIndexMap->valType == STRING){
				char* result;
				pkt->getStringAttributeVal(i, result);
				std::string stringVal = result;
				boost::unordered_map<std::string, IndexTypeStruct* >::iterator itCurr = currentIndexMap->stringToIndex->find(stringVal);
				if (itCurr != currentIndexMap->stringToIndex->end()){
	//				cout << "Valore di tipo STRING già presente nell'indice" << endl;
					currentIndexMap = itCurr->second;
				} else {
	//				cout << "Valore di tipo STRING non trovato nell'indice" << endl;
					IndexType* indexType = (IndexType*)malloc(sizeof(IndexType));
					if (i<pkt->getAttributesNum()-1) {
		//				cout << "Creazione nuovo INDICE tipo: ";
						indexType->contentType = INDEX;
						indexType->valType = pkt->getAttribute(i+1).type;
		//				cout << indexType->valType << endl;
						if (indexType->valType==INT) indexType->intToIndex = new std::map<int, IndexTypeStruct* >();
						if (indexType->valType==FLOAT) indexType->floatToIndex = new std::map<float, IndexTypeStruct* >();
						if (indexType->valType==BOOL) indexType->boolToIndex = new boost::unordered_map<bool, IndexTypeStruct* >();
						if (indexType->valType==STRING) indexType->stringToIndex = new boost::unordered_map<std::string, IndexTypeStruct* >();
					}
					else {
						indexType->contentType = TERMINAL;
						indexType->keySet = new std::set<std::pair<timestamp_t, key_t> >();
	//					cout << "Creazione TERMINALE" << endl;
					}
					indexType->prev = currentIndexMap;
					std::pair<boost::unordered_map<std::string, IndexTypeStruct* >::iterator, bool> ret = currentIndexMap->stringToIndex->insert(make_pair(stringVal, indexType));
					indexType->itStringToIndex = ret.first;
					currentIndexMap = indexType;
				}
				continue;
			}
		}
	}

	cacheTable.insert(make_pair(k, make_pair(pkt, pointerToIndex)));
	cout << "Memory: Cache: insertion into cache completed." << endl;

	timer->stop();
	cout << "Memory: Cache: insertion terminated in " << timer->getElapsedTimeInMilliSec() << " ms." << endl;
	return true;
}

bool Cache::remove(std::pair<timestamp_t, key_t> key){
	timer->start();

//	cout << "Inizio rimozione..." << endl;
	boost::unordered_map<key_t, std::pair<PubPkt*, IndexType*> >::iterator it = cacheTable.find(key.second);
	if (it != cacheTable.end()){
//		cout << "Chiave trovata" << endl;
		IndexType* currentIndex = it->second.second;
		currentIndex->keySet->erase(key);
//		cout << "Chiave rimossa" << endl;
		cout << "Memory: Cache: remotion from index completed." << endl;
		if (currentIndex->keySet->empty()){
		//	cout << "Indice da aggiornare..." << endl;
			delete currentIndex->keySet;
			bool toProgress = true;
			while(toProgress){
				if (currentIndex->prev == NULL){
					indexMap.erase(currentIndex->itToType);
				} else {
					if (currentIndex->prev->valType==INT){
						currentIndex->prev->intToIndex->erase(currentIndex->itIntToIndex);
						if (!currentIndex->prev->intToIndex->empty()) toProgress = false;
					}
					if (currentIndex->prev->valType==FLOAT){
						currentIndex->prev->floatToIndex->erase(currentIndex->itFloatToIndex);
						if (!currentIndex->prev->floatToIndex->empty()) toProgress = false;
					}
					if (currentIndex->prev->valType==BOOL){
						currentIndex->prev->boolToIndex->erase(currentIndex->itBoolToIndex);
						if (!currentIndex->prev->boolToIndex->empty()) toProgress = false;
					}
					if (currentIndex->prev->valType==STRING){
						currentIndex->prev->stringToIndex->erase(currentIndex->itStringToIndex);
						if (!currentIndex->prev->stringToIndex->empty()) toProgress = false;
					}
					IndexType* indexToFree = currentIndex;
					currentIndex = currentIndex->prev;
					free(indexToFree);
				}
			}
		//	cout << "... indice aggiornato" << endl;
			cout << "Memory: Cache: index updated." << endl;
		}
		PubPkt* pkt = it->second.first;
		cacheTable.erase(key.second);
		if (pkt->decRefCount()) delete pkt;
		cout << "Memory: Cache: remotion from cache completed." << endl;

		timer->stop();
		cout << "Memory: Cache: remotion terminated in " << timer->getElapsedTimeInMilliSec() << " ms." << endl;
		return true;
	}
	cout << "Memory: Cache: remotion failed: message not found." << endl;
	return false;

}

void Cache::extract(int type, Constraint* parCostraint, int parNumConstraint, timestamp_t parEntryTimestamp, float window, OccurrenceType occurrence, std::set<PubPkt*> &resultSet){
	timer->start();

	cout << "Memory: Cache: start index exploration..." << endl;
//	std::set<PubPkt*> resultSet;

	boost::unordered_map<int, IndexType*>::iterator it = indexMap.find(type);
	if (it != indexMap.end()){
//		cout << "Tipo trovato" << endl;
		IndexType* currentIndex = it->second;
		subSearch(currentIndex, parCostraint, 0, parNumConstraint, parEntryTimestamp, window, occurrence, resultSet);
	}
	timer->stop();
	cout << "Memory: Cache: extraction terminated in " << timer->getElapsedTimeInMilliSec() << " ms." << endl;
//	return resultSet;

}

void Cache::subSearch(IndexType* index, Constraint* parConstraint, int current, int parNumConstraint, timestamp_t parEntryTimestamp, float window, OccurrenceType occurrence, std::set<PubPkt*> &resultSet){

//	cout << ">> nuova sottoricerca: ";
//	cout << current << endl;
	if (current == parNumConstraint){
		if (index->contentType == TERMINAL){
			if (occurrence == LAST){
				for (std::set<std::pair<timestamp_t, key_t> >::reverse_iterator it = index->keySet->rbegin(); it != index->keySet->rend(); it++){
					timestamp_t timestamp = it->first;
					if ((timestamp <= parEntryTimestamp) && (timestamp >= (parEntryTimestamp - (int long)window))){
						key_t k = it->second;
						boost::unordered_map<key_t, pair<PubPkt*, IndexType*> >::iterator it2 = cacheTable.find(k);
						if (it2 != cacheTable.end()){
	//						cout << "beccato!!" << endl;
							resultSet.insert(it2->second.first);
							break;
						}
					}
				}
			} else {
				for (std::set<std::pair<timestamp_t, key_t> >::iterator it = index->keySet->begin(); it != index->keySet->end(); it++){
					timestamp_t timestamp = it->first;
					if ((timestamp <= parEntryTimestamp) && (timestamp >= (parEntryTimestamp - (int long)window))){
						key_t k = it->second;
						boost::unordered_map<key_t, pair<PubPkt*, IndexType*> >::iterator it2 = cacheTable.find(k);
						if (it2 != cacheTable.end()){
	//						cout << "beccato!!" << endl;
							resultSet.insert(it2->second.first);
						}
					}
				}
			}
		}
		return;
	} else {
		Constraint constaint = parConstraint[current];
		if (constaint.op == EQ) {
			if (constaint.type == STRING){
//				cout << "costraint EQ - STRING" << endl;
//				cout << "index valType: ";
//				cout << index->valType << endl;
				boost::unordered_map<std::string, IndexTypeStruct* >::iterator it = index->stringToIndex->find(constaint.stringVal);
				if (it != index->stringToIndex->end()){
//					cout << "costraint trovato" << endl;
					subSearch(it->second, parConstraint, current+1, parNumConstraint,parEntryTimestamp, window, occurrence, resultSet);
				}
			}
			if (constaint.type == BOOL){
				boost::unordered_map<bool, IndexTypeStruct* >::iterator it = index->boolToIndex->find(constaint.boolVal);
				if (it != index->boolToIndex->end()){
					subSearch(it->second, parConstraint, current+1, parNumConstraint,parEntryTimestamp, window, occurrence, resultSet);
				}
			}
			if (constaint.type == INT){
				std::map<int, IndexTypeStruct* >::iterator it = index->intToIndex->find(constaint.intVal);
				if (it != index->intToIndex->end()){
					subSearch(it->second, parConstraint, current+1, parNumConstraint,parEntryTimestamp, window, occurrence, resultSet);
				}
			}
			if (constaint.type == FLOAT){
				std::map<float, IndexTypeStruct* >::iterator it = index->floatToIndex->find(constaint.floatVal);
				if (it != index->floatToIndex->end()){
					subSearch(it->second, parConstraint, current+1, parNumConstraint,parEntryTimestamp, window, occurrence, resultSet);
				}
			}
			return;
		}
		if (constaint.op == GT) {
			if (constaint.type == INT){
//				cout << "costraint GT - INT" << endl;
//				cout << "index valType: ";
//				cout << index->valType << endl;
				std::map<int, IndexTypeStruct* >::iterator intIt;
				std::map<int, IndexTypeStruct* >::iterator exactIt = index->intToIndex->find(constaint.intVal);
				if (exactIt != index->intToIndex->end()){
					intIt = exactIt;
				} else {
					intIt = index->intToIndex->begin();
					while (intIt->first < constaint.intVal && intIt != index->intToIndex->end()) intIt++;
				}


				for (std::map<int, IndexTypeStruct* >::iterator it = intIt; it != index->intToIndex->end(); it++){
					subSearch(it->second, parConstraint, current+1, parNumConstraint,parEntryTimestamp, window, occurrence, resultSet);
				}
			}
			if (constaint.type == FLOAT){
//				cout << "costraint GT - FLOAT" << endl;
//				cout << "index valType: ";
//				cout << index->valType << endl;
				std::map<float, IndexTypeStruct* >::iterator floatIt;
				std::map<float, IndexTypeStruct* >::iterator exactIt = index->floatToIndex->find(constaint.floatVal);
				if (exactIt != index->floatToIndex->end()){
					floatIt = exactIt;
				} else {
					floatIt = index->floatToIndex->begin();
					while (floatIt->first < constaint.floatVal && floatIt != index->floatToIndex->end()) floatIt++;
				}
				for (std::map<float, IndexTypeStruct* >::iterator it = floatIt; it != index->floatToIndex->end(); it++){
					subSearch(it->second, parConstraint, current+1, parNumConstraint,parEntryTimestamp, window, occurrence, resultSet);
				}
			}
			return;
		}
		if (constaint.op == LT) {
			if (constaint.type == INT){
//				cout << "costraint LT - INT" << endl;
//				cout << "index valType: ";
//				cout << index->valType << endl;
//				cout << index->intToIndex->size() << endl;
				std::map<int, IndexTypeStruct* >::iterator intIt;
				std::map<int, IndexTypeStruct* >::iterator exactIt = index->intToIndex->find(constaint.intVal);
				if (exactIt != index->intToIndex->end()){
					intIt = exactIt;
				} else {
					intIt = index->intToIndex->end(); intIt--;
					while (intIt->first > constaint.intVal && intIt != index->intToIndex->begin()){ intIt--; }
				}
				if (intIt != index->intToIndex->begin()) intIt++;
				for (std::map<int, IndexTypeStruct* >::iterator it = index->intToIndex->begin(); it != intIt; it++){
//					cout << "costraint trovato" << endl;
					subSearch(it->second, parConstraint, current+1, parNumConstraint,parEntryTimestamp, window, occurrence, resultSet);
				}
			}
			if (constaint.type == FLOAT){
//				cout << "costraint LT - FLOAT" << endl;
//				cout << "index valType: ";
//				cout << index->valType << endl;
				std::map<float, IndexTypeStruct* >::iterator floatIt;
				std::map<float, IndexTypeStruct* >::iterator exactIt = index->floatToIndex->find(constaint.floatVal);
				if (exactIt != index->floatToIndex->end()){
					floatIt = exactIt;
				} else {
					floatIt = index->floatToIndex->end(); floatIt--;
					while (floatIt->first > constaint.floatVal&&  floatIt != index->floatToIndex->begin()) floatIt--;
				}
				if (floatIt != index->floatToIndex->begin()) floatIt++;
				for (std::map<float, IndexTypeStruct* >::iterator it = index->floatToIndex->begin(); it != floatIt; it++){
					subSearch(it->second, parConstraint, current+1, parNumConstraint,parEntryTimestamp, window, occurrence, resultSet);
				}
			}
			return;
		}
	}

}




















